/**
 假设现在有两个用户账户A和B，每个账户都有一定的余额，现在需要实现一个线程安全的转账功能，即一个用户可以将一定的金额转入到另一个用户账户中。要求如下：
 1. 用户可以同时进行多次转账操作，每次转账的金额必须大于0，同时转出账户余额必须大于等于转账金额。
 2. 转账过程中，需要对转出账户和转入账户加锁，保证同时只能有一个线程对某个账户进行转账操作。
 3. 在进行转账操作时，需要按照账户名称的字典序进行排序，保证转账过程中的资源占用顺序一致，避免死锁的出现。
 4. 转账操作完成后，需要释放相应的锁，并输出转账的金额和最新的账户余额。
 要求设计一个银行账户转账类，包含以下方法：
 transfer(Account from, Account to, double amount) ：从账户from中转出一定的金额， 将其转入到账户to中。
 其中，Account是一个用户账户类，包含以下属性：
 String name ：账户名称。
 double balance ：账户余额。
 编写测试程序，模拟多个用户同时进行转账操作，验证银行账户转账类的线程安全性。

 */
public class Test6 {
    // 转账方法
    public static boolean transfer(Account from, Account to, double amount) {
        // 验证转账金额
        if (amount <= 0) {
            System.out.println("转账金额必须大于0");
            return false;
        }

        // 按账户名称字典序排序，避免死锁
        Account firstLock, secondLock;
        if (from.name.compareTo(to.name) < 0) {
            firstLock = from;
            secondLock = to;
        } else {
            firstLock = to;
            secondLock = from;
        }

        synchronized (firstLock) {
            synchronized (secondLock) {
                // 验证余额
                if (from.balance < amount) {
                    System.out.println(from.name + " 余额不足，转账失败");
                    return false;
                }

                // 执行转账
                from.balance -= amount;
                to.balance += amount;

                System.out.printf("%s 向 %s 转账 %.2f 元，", from.name, to.name, amount);
                System.out.printf("%s 余额: %.2f，%s 余额: %.2f%n",
                        from.name, from.balance, to.name, to.balance);
                return true;
            }
        }
    }

    public static void main(String[] args) {
        // 创建账户
        Account a = new Account("A", 1000);
        Account b = new Account("B", 1000);

        // 创建多个转账线程
        for (int i = 0; i < 5; i++) {
            new Thread(() -> {
                transfer(a, b, 100);
            }).start();

            new Thread(() -> {
                transfer(b, a, 50);
            }).start();
        }
    }
}

// 账户类
class Account {
    String name;
    double balance;

    public Account(String name, double balance) {
        this.name = name;
        this.balance = balance;
    }
}
